home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
pppfsm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-07
|
18KB
|
848 lines
/*
* PPPFSM.C -- PPP Finite State Machine
*
* This implementation of PPP is declared to be in the public domain.
*
* Jan 91 Bill_Simpson@um.cc.umich.edu
* Computer Systems Consulting Services
*
* Acknowledgements and correction history may be found in PPP.C
*/
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "ppp.h"
#include "pppfsm.h"
#include "ppplcp.h"
char *fsmStates[] = {
"Closed",
"Listen",
"Req Sent",
"Ack Rcvd",
"Ack Sent",
"Open",
"TermSent"
};
char *fsmCodes[] = {
NULLCHAR,
"Configure Request",
"Configure Ack",
"Configure Nak",
"Configure Reject",
"Terminate Request",
"Terminate Ack",
"Code Reject",
"Protocol Reject",
"Echo Request",
"Echo Reply",
"Discard Request",
};
static int fsm_sendtermreq __ARGS((struct fsm_s *fsm_p));
static int fsm_sendtermack __ARGS((struct fsm_s *fsm_p, byte_t id));
static void fsm_timeout __ARGS((void *vp));
static void fsm_reset __ARGS((struct fsm_s *fsm_p));
static void fsm_opening __ARGS((struct fsm_s *fsm_p));
/************************************************************************/
/* Convert header in host form to network form */
struct mbuf *
htoncnf(cnf, data)
struct config_hdr *cnf;
struct mbuf *data;
{
struct mbuf *bp;
register char *cp;
/* Prepend bytes for LCP/IPCP header */
if ((bp = pushdown(data, CONFIG_HDR_LEN)) == NULLBUF)
return NULLBUF;
/* Load header with proper values */
cp = bp->data;
*cp++ = cnf->code;
*cp++ = cnf->id;
put16(cp, cnf->len);
return bp;
}
/* Extract header from incoming packet */
int
ntohcnf(cnf, bpp)
struct config_hdr *cnf;
struct mbuf **bpp;
{
if (cnf== NULL || bpp == NULLBUFP
|| *bpp == NULLBUF)
return -1;
cnf->code = pullchar(bpp);
cnf->id = pullchar(bpp);
cnf->len = pull16(bpp);
return 0;
}
/***************************************/
/* Convert configuration option header in host form to network form */
struct mbuf *
htonopt(opt)
struct option_hdr *opt;
{
struct mbuf *bp;
register char *cp;
if((bp = alloc_mbuf(OPTION_HDR_LEN)) == NULLBUF)
return NULLBUF;
/* Load data into LCP/IPCP configuration option header */
cp = bp->data;
*cp++ = opt->type;
*cp++ = opt->len;
bp->cnt = cp - bp->data;
return bp;
}
/* Extract configuration option header */
int
ntohopt(opt,bpp)
struct option_hdr *opt;
struct mbuf **bpp;
{
if (opt== NULLOPTHDR || bpp == NULLBUFP
|| *bpp == NULLBUF)
return -1;
opt->type = pullchar(bpp);
opt->len = pullchar(bpp);
return 0;
}
/************************************************************************/
void
fsm_no_action(fsm_p)
struct fsm_s *fsm_p;
{
PPP_DEBUG_ROUTINES("fsm_no_action()");
}
int
fsm_no_check(fsm_p, hdr, bp)
struct fsm_s *fsm_p;
struct config_hdr *hdr;
struct mbuf *bp;
{
PPP_DEBUG_ROUTINES("fsm_no_check()");
return 0;
}
/************************************************************************/
/* General log routine */
void
fsm_log(fsm_p, comment)
struct fsm_s *fsm_p;
char *comment;
{
if (PPPtrace)
log(-1,"%s PPP/%s %-8s; %s",
fsm_p->ppp_p->iface->name,
fsm_p->pdc->name,
fsmStates[fsm_p->state],
comment);
}
/************************************************************************/
/* Set a timer in case an expected event does not occur */
void
fsm_timer(fsm_p)
struct fsm_s *fsm_p;
{
PPP_DEBUG_ROUTINES("fsm_timer()");
start_timer( &(fsm_p->timer) );
}
/************************************************************************/
/* Send a packet to the remote host */
int
fsm_send(fsm_p,code,id,data)
struct fsm_s *fsm_p;
byte_t code;
byte_t id;
struct mbuf *data;
{
struct ppp_s *ppp_p = fsm_p->ppp_p;
struct iface *iface = ppp_p->iface;
struct config_hdr hdr;
switch( hdr.code = code ) {
case CONFIG_REQ:
case TERM_REQ:
case ECHO_REQ:
/* Save ID field for match against replies from remote host */
fsm_p->lastid = ppp_p->id;
/* fallthru */
case PROT_REJ:
case DISCARD_REQ:
/* Use a unique ID field value */
hdr.id = ppp_p->id++;
break;
case CONFIG_ACK:
case CONFIG_NAK:
case CONFIG_REJ:
case TERM_ACK:
case CODE_REJ:
case ECHO_REPLY:
/* Use ID sent by remote host */
hdr.id = id;
break;
default:
/* we're in trouble */
log(-1, "%s PPP/%s %-8s;"
" Send with bogus code: %d",
iface->name,
fsm_p->pdc->name,
fsmStates[fsm_p->state],
code);
return -1;
};
switch( code ) {
case ECHO_REQ:
case ECHO_REPLY:
case DISCARD_REQ:
{
struct lcp_s *lcp_p = fsm_p->pdv;
data = pushdown(data, 4);
put32(data->data, lcp_p->local.work.magic_number);
}
};
hdr.len = len_p(data) + CONFIG_HDR_LEN;
/* Prepend header to packet data */
if ((data = htoncnf(&hdr,data)) == NULLBUF)
return -1;
if (PPPtrace > 1) {
log(-1, "%s PPP/%s %-8s;"
" Sending option: %s, id: %d, len: %d",
iface->name,
fsm_p->pdc->name,
fsmStates[fsm_p->state],
fsmCodes[code],
hdr.id,hdr.len);
}
ppp_p->OutNCP[fsm_p->pdc->fsmi]++;
return( (*iface->output)
(iface, NULLCHAR, NULLCHAR, fsm_p->pdc->protocol, data) );
}
/************************************************************************/
/* Send a configuration request */
int
fsm_sendreq(fsm_p)
struct fsm_s *fsm_p;
{
struct mbuf *bp;
PPP_DEBUG_ROUTINES("fsm_sendreq()");
if ( fsm_p->retry <= 0 )
return -1;
fsm_p->retry--;
fsm_timer(fsm_p);
bp = (*fsm_p->pdc->makereq)(fsm_p);
return( fsm_send(fsm_p, CONFIG_REQ, 0, bp) );
}
/************************************************************************/
/* Send a termination request */
static int
fsm_sendtermreq(fsm_p)
struct fsm_s *fsm_p;
{
PPP_DEBUG_ROUTINES("fsm_sendtermreq()");
if ( fsm_p->retry <= 0 )
return -1;
fsm_p->retry--;
fsm_timer(fsm_p);
return( fsm_send(fsm_p, TERM_REQ, 0, NULLBUF) );
}
/************************************************************************/
/* Send Terminate Ack */
static int
fsm_sendtermack(fsm_p,id)
struct fsm_s *fsm_p;
byte_t id;
{
PPP_DEBUG_ROUTINES("fsm_sendtermack()");
return( fsm_send(fsm_p, TERM_ACK, id, NULLBUF) );
}
/************************************************************************/
/* Reset state machine */
static void
fsm_reset(fsm_p)
struct fsm_s *fsm_p;
{
PPP_DEBUG_ROUTINES("fsm_reset()");
fsm_p->state = (fsm_p->flags & (FSM_ACTIVE | FSM_PASSIVE))
? fsmLISTEN : fsmCLOSED;
fsm_p->retry = fsm_p->try_req;
fsm_p->retry_nak = fsm_p->try_nak;
(*fsm_p->pdc->reset)(fsm_p);
}
/************************************************************************/
/* Configuration negotiation complete */
static void
fsm_opening(fsm_p)
struct fsm_s *fsm_p;
{
fsm_log(fsm_p, "Open");
stop_timer(&(fsm_p->timer));
(*fsm_p->pdc->opening)(fsm_p);
fsm_p->state = fsmOPENED;
}
/************************************************************************/
/* E V E N T P R O C E S S I N G */
/************************************************************************/
/* Process incoming packet */
void
fsm_proc(fsm_p,bp)
struct fsm_s *fsm_p;
struct mbuf *bp;
{
struct config_hdr hdr;
PPPtrace = fsm_p->ppp_p->trace;
ntohcnf(&hdr, &bp);
if (PPPtrace > 1)
log(-1, "%s PPP/%s %-8s;"
" Processing option: %s, id: %d, len: %d",
fsm_p->ppp_p->iface->name,
fsm_p->pdc->name,
fsmStates[fsm_p->state],
fsmCodes[hdr.code],
hdr.id, hdr.len);
hdr.len -= CONFIG_HDR_LEN; /* Length includes envelope */
trim_mbuf(&bp, hdr.len); /* Trim off padding */
switch(hdr.code) {
case CONFIG_REQ:
switch(fsm_p->state) {
case fsmOPENED: /* Unexpected event? */
(*fsm_p->pdc->closing)(fsm_p);
fsm_reset(fsm_p);
/* fallthru */
case fsmLISTEN:
fsm_sendreq(fsm_p);
/* fallthru */
case fsmREQ_Sent:
case fsmACK_Sent: /* Unexpected event? */
fsm_p->state =
((*fsm_p->pdc->request)(fsm_p, &hdr, bp) == 0)
? fsmACK_Sent : fsmREQ_Sent;
break;
case fsmACK_Rcvd:
if ((*fsm_p->pdc->request)(fsm_p, &hdr, bp) == 0) {
fsm_opening(fsm_p);
} else {
/* give peer time to respond */
fsm_timer(fsm_p);
}
break;
case fsmCLOSED:
/* Don't accept any connections */
fsm_sendtermack(fsm_p, hdr.id);
/* fallthru */
case fsmTERM_Sent:
/* We are attempting to close connection; */
/* wait for timeout to resend a Terminate Request */
free_p(bp);
break;
};
break;
case CONFIG_ACK:
switch(fsm_p->state) {
case fsmREQ_Sent:
if ((*fsm_p->pdc->ack)(fsm_p, &hdr, bp) == 0) {
fsm_p->state = fsmACK_Rcvd;
}
break;
case fsmACK_Sent:
if ((*fsm_p->pdc->ack)(fsm_p, &hdr, bp) == 0) {
fsm_opening(fsm_p);
}
break;
case fsmOPENED: /* Unexpected event? */
(*fsm_p->pdc->closing)(fsm_p);
fsm_reset(fsm_p);
/* fallthru */
case fsmACK_Rcvd: /* Unexpected event? */
free_p(bp);
fsm_sendreq(fsm_p);
fsm_p->state = fsmREQ_Sent;
break;
case fsmCLOSED:
case fsmLISTEN:
/* Out of Sync; kill the remote */
fsm_sendtermack(fsm_p, hdr.id);
/* fallthru */
case fsmTERM_Sent:
/* We are attempting to close connection; */
/* wait for timeout to resend a Terminate Request */
free_p(bp);
break;
};
break;
case CONFIG_NAK:
switch(fsm_p->state) {
case fsmREQ_Sent:
case fsmACK_Sent:
/* Update our config request to reflect NAKed options */
if ((*fsm_p->pdc->nak)(fsm_p, &hdr, bp) == 0) {
/* Send updated config request */
fsm_sendreq(fsm_p);
}
break;
case fsmOPENED: /* Unexpected event? */
(*fsm_p->pdc->closing)(fsm_p);
fsm_reset(fsm_p);
/* fallthru */
case fsmACK_Rcvd: /* Unexpected event? */
free_p(bp);
fsm_sendreq(fsm_p);
fsm_p->state = fsmREQ_Sent;
break;
case fsmCLOSED:
case fsmLISTEN:
/* Out of Sync; kill the remote */
fsm_sendtermack(fsm_p, hdr.id);
/* fallthru */
case fsmTERM_Sent:
/* We are attempting to close connection; */
/* wait for timeout to resend a Terminate Request */
free_p(bp);
break;
};
break;
case CONFIG_REJ:
switch(fsm_p->state) {
case fsmREQ_Sent:
case fsmACK_Sent:
if((*fsm_p->pdc->reject)(fsm_p, &hdr, bp) == 0) {
fsm_sendreq(fsm_p);
}
break;
case fsmOPENED: /* Unexpected event? */
(*fsm_p->pdc->closing)(fsm_p);
fsm_reset(fsm_p);
/* fallthru */
case fsmACK_Rcvd: /* Unexpected event? */
free_p(bp);
fsm_sendreq(fsm_p);
fsm_p->state = fsmREQ_Sent;
break;
case fsmCLOSED:
case fsmLISTEN:
/* Out of Sync; kill the remote */
fsm_sendtermack(fsm_p, hdr.id);
/* fallthru */
case fsmTERM_Sent:
/* We are attempting to close connection; */
/* wait for timeout to resend a Terminate Request */
free_p(bp);
break;
};
break;
case TERM_REQ:
fsm_log(fsm_p, "Peer requested Termination");
switch(fsm_p->state) {
case fsmOPENED:
(*fsm_p->pdc->closing)(fsm_p);
fsm_sendtermack(fsm_p, hdr.id);
fsm_reset(fsm_p);
break;
case fsmACK_Rcvd:
case fsmACK_Sent:
fsm_p->state = fsmREQ_Sent;
/* fallthru */
case fsmREQ_Sent:
case fsmTERM_Sent:
/* waiting for timeout */
/* fallthru */
case fsmCLOSED:
case fsmLISTEN:
/* Unexpected, but make them happy */
fsm_sendtermack(fsm_p, hdr.id);
break;
};
break;
case TERM_ACK:
switch(fsm_p->state) {
case fsmTERM_Sent:
stop_timer(&(fsm_p->timer));
fsm_log(fsm_p, "Terminated");
fsm_reset(fsm_p);
(*fsm_p->pdc->stopping)(fsm_p);
break;
case fsmOPENED:
/* Remote host has abruptly closed connection */
fsm_log(fsm_p, "Terminated unexpectly");
(*fsm_p->pdc->closing)(fsm_p);
fsm_reset(fsm_p);
if ( fsm_sendreq(fsm_p) == 0 ) {
fsm_p->state = fsmREQ_Sent;
}
break;
case fsmACK_Sent:
case fsmACK_Rcvd:
fsm_p->state = fsmREQ_Sent;
/* fallthru */
case fsmREQ_Sent:
/* waiting for timeout */
/* fallthru */
case fsmCLOSED:
case fsmLISTEN:
/* Unexpected, but no action needed */
break;
};
break;
case CODE_REJ:
log(-1,"%s: PPP/%s Code Reject;"
" indicates faulty implementation",
fsm_p->ppp_p->iface->name,
fsm_p->pdc->name);
fsm_reset(fsm_p);
(*fsm_p->pdc->stopping)(fsm_p);
free_p(bp);
break;
case PROT_REJ:
log(-1,"%s: PPP/%s Protocol Reject;"
" please do not use this protocol",
fsm_p->ppp_p->iface->name,
fsm_p->pdc->name);
free_p(bp);
break;
case ECHO_REQ:
switch(fsm_p->state) {
case fsmOPENED:
fsm_send( fsm_p, ECHO_REPLY, hdr.id, bp );
break;
case fsmCLOSED:
case fsmLISTEN:
/* Out of Sync; kill the remote */
fsm_sendtermack(fsm_p, hdr.id);
/* fallthru */
case fsmREQ_Sent:
case fsmACK_Rcvd:
case fsmACK_Sent:
case fsmTERM_Sent:
/* ignore */
free_p(bp);
break;
};
break;
case ECHO_REPLY:
case DISCARD_REQ:
case QUALITY_REPORT:
free_p(bp);
break;
default:
log(-1,"%s: PPP/%s Unknown packet type: %d;"
" sending Code Reject",
fsm_p->ppp_p->iface->name,
fsm_p->pdc->name,
hdr.code);
hdr.len += CONFIG_HDR_LEN; /* restore length */
bp = htoncnf( &hdr, bp ); /* put header back on */
fsm_send( fsm_p, CODE_REJ, hdr.id, bp );
switch(fsm_p->state) {
case fsmREQ_Sent:
case fsmACK_Rcvd:
case fsmACK_Sent:
case fsmOPENED:
fsm_p->state = fsmLISTEN;
break;
case fsmCLOSED:
case fsmLISTEN:
case fsmTERM_Sent:
/* no change */
break;
};
break;
}
}
/************************************************************************/
/* Timeout while waiting for reply from remote host */
static void
fsm_timeout(vp)
void *vp;
{
struct fsm_s *fsm_p = (struct fsm_s *)vp;
PPPtrace = fsm_p->ppp_p->trace;
fsm_log( fsm_p, "Timeout" );
switch(fsm_p->state) {
case fsmREQ_Sent:
case fsmACK_Rcvd:
case fsmACK_Sent:
if (fsm_p->retry > 0) {
fsm_sendreq(fsm_p);
fsm_p->state = fsmREQ_Sent;
} else {
fsm_log(fsm_p, "Request retry exceeded");
fsm_reset(fsm_p);
(*fsm_p->pdc->stopping)(fsm_p);
}
break;
case fsmTERM_Sent:
if (fsm_p->retry > 0) {
fsm_sendtermreq(fsm_p);
} else {
fsm_log(fsm_p, "Terminate retry exceeded");
fsm_reset(fsm_p);
(*fsm_p->pdc->stopping)(fsm_p);
}
break;
case fsmCLOSED:
case fsmLISTEN:
case fsmOPENED:
/* nothing to do */
break;
}
}
/************************************************************************/
/* I N I T I A L I Z A T I O N */
/************************************************************************/
/* Start FSM (after open event, and physical line up) */
void
fsm_start(fsm_p)
struct fsm_s *fsm_p;
{
if ( fsm_p->pdv == NULL )
return;
PPPtrace = fsm_p->ppp_p->trace;
fsm_log(fsm_p, "Start");
if ( !(fsm_p->flags & (FSM_ACTIVE | FSM_PASSIVE)) )
return;
switch ( fsm_p->state ) {
case fsmCLOSED:
case fsmLISTEN:
case fsmTERM_Sent:
fsm_reset(fsm_p);
(*fsm_p->pdc->starting)(fsm_p);
if ( fsm_p->flags & FSM_ACTIVE ){
fsm_sendreq(fsm_p);
fsm_p->state = fsmREQ_Sent;
}
break;
default:
/* already started */
break;
};
}
/************************************************************************/
/* Physical Line Down Event */
void
fsm_down(fsm_p)
struct fsm_s *fsm_p;
{
if ( fsm_p->pdv == NULL )
return;
PPPtrace = fsm_p->ppp_p->trace;
fsm_log(fsm_p, "Down");
switch ( fsm_p->state ) {
case fsmREQ_Sent:
case fsmACK_Rcvd:
case fsmACK_Sent:
stop_timer(&(fsm_p->timer));
fsm_reset(fsm_p);
(*fsm_p->pdc->stopping)(fsm_p);
break;
case fsmOPENED:
(*fsm_p->pdc->closing)(fsm_p);
/* fallthru */
case fsmTERM_Sent:
fsm_reset(fsm_p);
(*fsm_p->pdc->stopping)(fsm_p);
break;
case fsmCLOSED:
case fsmLISTEN:
/* nothing to do */
break;
};
}
/************************************************************************/
/* Close the connection */
void
fsm_close(fsm_p)
struct fsm_s *fsm_p;
{
if ( fsm_p->pdv == NULL )
return;
PPPtrace = fsm_p->ppp_p->trace;
fsm_log(fsm_p, "Close");
switch ( fsm_p->state ) {
case fsmOPENED:
(*fsm_p->pdc->closing)(fsm_p);
/* fallthru */
case fsmACK_Sent:
fsm_p->retry = fsm_p->try_terminate;
fsm_sendtermreq(fsm_p);
fsm_p->state = fsmTERM_Sent;
break;
case fsmREQ_Sent:
case fsmACK_Rcvd:
/* simply wait for REQ timeout to expire */
fsm_p->retry = 0;
fsm_p->state = fsmTERM_Sent;
break;
case fsmLISTEN:
fsm_p->state = fsmCLOSED;
break;
case fsmTERM_Sent:
case fsmCLOSED:
/* nothing to do */
break;
};
}
/************************************************************************/
/* Initialize the fsm for this protocol
* Called from protocol _init
*/
void
fsm_init(fsm_p)
struct fsm_s *fsm_p;
{
struct timer *t = &(fsm_p->timer);
PPP_DEBUG_ROUTINES("fsm_init()");
fsm_p->try_req = fsm_p->pdc->try_req;
fsm_p->try_nak = fsm_p->pdc->try_nak;
fsm_p->try_terminate = fsm_p->pdc->try_terminate;
fsm_reset(fsm_p);
/* Initialize timer */
t->func = (void (*)())fsm_timeout;
t->arg = (void *)fsm_p;
set_timer(t, fsm_p->pdc->timeout);
fsm_timer(fsm_p);
stop_timer(t);
}
void
fsm_free(fsm_p)
struct fsm_s *fsm_p;
{
if ( fsm_p->pdv != NULL ) {
(*fsm_p->pdc->free)(fsm_p);
free( fsm_p->pdv );
fsm_p->pdv = NULL;
}
}